<?php
/*
 * Copyright (c) 2008 Sergey Bondari, bondari@bondari.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

namespace Mosaic\System;

/**
 * Parent for all remote exception.
 *
 * Parent class for all exeption which can be transferred between nodes.
 * All other exceptions are handled locally. 
 *
 * @package System
 * @subpackage Exceptions
 */
class RemoteExceptionThrowable extends \Exception {
	/**
	 * Remote exception container
	 * @var RemoteExceptionContainer
	 */
	protected $rEx;

	/**
	 * Exception constructor
	 * @param string Exception string message
	 * @param string Exception code
	 * @param RemoteExceptionContainer
	 */
	function __construct($msg, $code = 0, RemoteExceptionContainer $e = NULL) {
		parent::__construct($msg, $code);
		$this->setRemoteExceptionData($e);
	}

	/**
	 * Remote exception container setter
	 * @param RemoteExceptionContainer
	 */
	final protected function setRemoteExceptionData(RemoteExceptionContainer $e) {
		$this->rEx = $e;
	}

	/**
	 * Remote exception flag indicator
	 * @return boolean
	 */
	final public function isRemote() {
		return ($this->rEx instanceof RemoteExceptionContainer);
	}

	/**
	 * File name where remote exception happened
	 * @return string
	 */
	final public function getRemoteFile() {
		return $this->isRemote() ? $this->rEx->getFile() : NULL;
	}

	/**
	 * File line where remote exception happened
	 * @return string
	 */
	final public function getRemoteLine() {
		return $this->isRemote() ? $this->rEx->getLine() : NULL;
	}

	/**
	 * Backtrace of remote exception
	 * @return array
	 */
	final public function getRemoteTrace() {
		return $this->isRemote() ? $this->rEx->getTrace() : NULL;
	}

	/**
	 * Backtrace string of remote exception
	 * @return string 
	 */
	final public function getRemoteTraceAsString() {
		return $this->isRemote() ? $this->rEx->getTraceAsString() : NULL;
	}
	
}


/**
 * Remote exception container.
 *
 * Exceptions containment class. Exceptions travel between nodes in this container.
 * Container simply copies all exception data.
 *
 * @package System
 * @subpackage Exceptions
 */
class RemoteExceptionContainer {
	protected $class = 'RemoteException';
	protected $message = 'Unknown exception';
	protected $code = 0;
	protected $file;
	protected $line;
	protected $trace;
	protected $traceAsString;

	/**
	 * Constructor.
	 *
	 * Container is constructed from normal exceptions
	 * @param RemoteExceptionThrowable
	 */
	function __construct(RemoteExceptionThrowable $e) {
		$this->class = get_class($e);
		$this->file = $e->getFile();
		$this->line = $e->getLine();
		$this->code = $e->getCode();
		$this->message = $e->getMessage();
		$this->trace = $e->getTrace();
		$this->traceAsString = $e->getTraceAsString();
	}


	final public function getMessage() {
		return $this->message;
	}

	public function getFile() {
		return $this->file;
	}

	public function getCode() {
		return $this->code;
	}

	public function getLine() {
		return $this->line;
	}

	public function getTrace() {
		return $this->trace;
	}

	public function getTraceAsString() {
		return $this->traceAsString;
	}

	/**
	 * Starts exception based on the data contained in the instance
	 */
	public function raise() {
		$class = $this->class;	
		if (!class_exists($class)) $class = 'RemoteException';
		throw new $class($this->getMessage(), $this->getCode(), $this);
	}
}
